<?
  //extract the correct parameter value. Convert entity id to entity name...
  function prepareMacro($macro,$parameter) {
    global $db;
    
    $qry="SELECT * FROM cw_macro WHERE id=$macro";
    $resM=$db->Execute($qry);
    $arrM=$resM->FetchRow();
    $macroName=$arrM["name"];

    //extract the parameters
    $params=xml2array($arrM["parameter"]);
    $pArr=split("\|",$parameter);
    if (is_array($pArr))
    foreach ($pArr as $v) {
      $keyvalue=split("=",$v);
      $selParamsArr[$keyvalue[0]]=$keyvalue[1];
    } 
    
    $arg="";
    if (is_array($params))
    foreach($params as $p) {
      if ($p["name"]!="param") continue;
      $pName=$p["attributes"]["name"];
      $pType=$p["attributes"]["type"];
      switch ($pType) {
        case "QUEUE":
          $r=$db->Execute("SELECT * FROM cw_queue WHERE id=".$selParamsArr[$pName]);
          $q=$r->FetchRow();
          $arg.="|\"queue_".$q["id"]."\"";
        break;
        case "GROUP":
        	$grp="";
          $resG=$db->Execute("SELECT * FROM cw_group_entity WHERE group_id=".$selParamsArr[$pName]);
          while ($arrG=$resG->FetchRow()) {
            $r=$db->Execute("SELECT name FROM `cw_channel` WHERE `key`=".$arrG["entity_id"]);
            $a=$r->FetchRow();
            $grp.=$a["name"]."&";
          }
          if($grp!="") $grp=substr($grp,0,-1);
          $arg.="|".$grp;
        break;
        case "EXTENSION":
          $r=$db->Execute("SELECT * FROM `cw_channel` WHERE `key`=".$selParamsArr[$pName]);
          $a=$r->FetchRow();
          $arg.="|".$a["name"];
        break;
        case "IVR":
          $arg.="|ivr_".$selParamsArr[$pName];
        break;
        case "EMAIL_EXTENSION":
        break;
        case "SOUNDFILE":
        case "TEXTFIELD":
        case "TRUNK":
        case "OUTGOING_RULE":
        case "INCOMING_RULE":
        case "CONFERENCE":
          $arg.="|\"".varEval($selParamsArr[$pName])."\"";
        break;
        default:
          agiError("ERROR: unknown macro parameter type \"".$pType."\"");
        break;
      }
    }  
    //$arg=str_replace("|",",",$arg);
    return array($macroName,$arg);      
  }
  
  //search for some variable and evaluate it
  function varEval($txt) {
    global $agi;
    if((strpos($txt,"\${")!==false)&&($agi!=null)){
    	$txtArr=split('\$\{',$txt);
    	agiError("params are: ".$txt." ");
    	$ret=$agi->get_full_variable($txt);
    	agiError("params are: ".$ret["data"]." ");
    	if($ret["data"]==""){
        	if(is_array($txtArr)){
			$txt=trim($txtArr[0]);
	          	for($i=1;$i<count($txtArr);$i++){
        		    $elemArr=split('\}',$txtArr[$i]);
        		    if((is_array($elemArr))&&(count($elemArr)==2)){
		          	$ret=$agi->get_variable($elemArr[0]);
          			agiError("param is: ".$elemArr[0]." \(".$ret["data"]."\)");
		              	$txt.=$ret["data"];
    			        $txt.=$elemArr[1];
		    	    }else{
		    		$txt.="\${".$txtArr[$i];
		    	    }
    			}
    		}
        }else{
    	    $txt=$ret["data"];
    	    agiError("result is ".$txt);
        }
    }
    /*
    if(substr(trim($txt),0,2)=="\${" and $agi!=null) {
      $ret=$agi->get_variable(substr(trim($txt),2,-1));
      $txt=$ret["data"];
    }
    */
    return $txt;
  }

  function _getGroup($entity_id) {
    global $db;
    $ret=array();
    if ($entity_id=="") return 0;
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=$entity_id";
    $res=$db->Execute($qry);
    while ($arr=$res->FetchRow()) $ret[]=$arr["group_id"];
    return $ret;
  }

  function _getTrunk($r) {
    global $db;
    $ret="";
    $arr=split("\|",$r);
    if (is_array($arr)) 
    foreach ($arr as $t) {
      $qry="SELECT * FROM cw_channel WHERE id=$t";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $ret.=$arr["name"]."&";
    }
    if($ret!="") $ret=substr($ret,0,-1);
    return $ret;
  }
    
  function checkTime($ts=0) {
    global $db;
    if ($ts==0) return true;
    //Get the Time Segment
    $qry="SELECT * FROM cw_time_segment WHERE id=$ts";
    $res=$db->Execute($qry);
    $arrTs=$res->FetchRow();
    $id=$arrTs["id"];
    $name=$arrTs["name"];
    $date_from=$arrTs["date_from"];
    $date_to=$arrTs["date_to"];
    $hour_from=$arrTs["hour_from"];
    $hour_to=$arrTs["hour_to"];
    $week_day=$arrTs["week_day"];
    $day=$arrTs["day"];
    $month=$arrTs["month"];  
    //Check Week Days
    if($week_day!=""){
      $qryTmp="";
      $week_dayArr=split("\|",$week_day);
      foreach ($week_dayArr as $w) {
        $qryTmp.=" OR DAYOFWEEK(NOW())='$w'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    }
    //Check Month Days
    if($day!=""){
      $qryTmp="";
      $dayArr=split("\|",$day);
      foreach ($dayArr as $d) {
        $qryTmp.=" OR DAYOFMONTH(NOW())='$d'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    } 
    //Check Month 
    if($month!=""){
      $qryTmp="";
      $monthArr=split("\|",$month);
      foreach ($monthArr as $m) {
        $qryTmp.=" OR MONTH(NOW())='$m'";
      }    
      $qry.=" AND (".substr($qryTmp,3).")";
    } 
    //Check Date Segment
    if(($date_from!="")OR($date_to!="")OR($hour_from!="")OR($hour_to!="")){
      $qryAllSeg="";
      $date_fromArr=split("\|",$date_from);
      $date_toArr=split("\|",$date_to);
      $hour_fromArr=split("\|",$hour_from);
      $hour_toArr=split("\|",$hour_to); 
      foreach($date_fromArr as $i=>$d) {
        $qrySeg="";
        if ($d != "")                $qrySeg.=" AND DATE(NOW())>=STR_TO_DATE('".$d."','%d-%m-%Y')";
        if ($date_toArr[$i] != "")   $qrySeg.=" AND DATE(NOW())<=STR_TO_DATE('".$date_toArr[$i]."','%d-%m-%Y')";
        if ($hour_fromArr[$i] != "") $qrySeg.=" AND TIME(NOW())>=".extractTime($hour_fromArr[$i]);
        if ($hour_toArr[$i] != "")   $qrySeg.=" AND TIME(NOW())<=".extractTime($hour_toArr[$i]);
        if($qrySeg!="") $qryAllSeg.=" OR (".substr($qrySeg,4).")";
      }
      if($qryAllSeg!="") $qry.=" AND (".substr($qryAllSeg,3).")";
    }
    //Execute query
    $res=$db->Execute($qry);
    //Return    
    if($res===false){
      agiError("  TIMESEGMENT '$name' CHECK: ERROR");
      return false;
    }else if($res->RecordCount()==0){
      agiError("  TIMESEGMENT '$name' CHECK: False");
      return false;
    }else{
      agiError("  TIMESEGMENT '$name' CHECK: True");
      return true;
    }
  }


  
  if (!function_exists("xml2array")) {
  function xml2array ($xml)
  {
     $xmlary = array ();
  
     if ((strlen ($xml) < 256) && is_file ($xml))
       $xml = file_get_contents ($xml);
    
     $ReElements = '/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*?)<(\/\s*\1\s*)>)/s';
     $ReAttributes = '/(\w+)=(?:"|\')([^"\']*)(:?"|\')/';
    
     preg_match_all ($ReElements, $xml, $elements);
     foreach ($elements[1] as $ie => $xx) {
     $xmlary[$ie]["name"] = $elements[1][$ie];
       if ( $attributes = trim($elements[2][$ie])) {
           preg_match_all ($ReAttributes, $attributes, $att);
           foreach ($att[1] as $ia => $xx)
             // all the attributes for current element are added here
             $xmlary[$ie]["attributes"][$att[1][$ia]] = $att[2][$ia];
       } // if $attributes
       
       // get text if it's combined with sub elements
     $cdend = strpos($elements[3][$ie],"<");
     if ($cdend > 0) {
             $xmlary[$ie]["text"] = substr($elements[3][$ie],0,$cdend -1);
         } // if cdend
         
       if (preg_match ($ReElements, $elements[3][$ie])){        
           $xmlary[$ie]["elements"] = xml2array ($elements[3][$ie]);
           }
       else if (isset($elements[3][$ie])){
           $xmlary[$ie]["text"] = $elements[3][$ie];
           }
       $xmlary[$ie]["closetag"] = $elements[4][$ie];
     }//foreach ?
     return $xmlary;
  } 
  }
    
function extractTime($t) {
  $arg="";
  $t=str_replace(",",":",$t);
  $t=str_replace(".",":",$t);
  $arr=split(":",$t);
  for ($i=0; $i<3; $i++) {
    if (isset($arr[$i])) $arg.=$arr[$i].",";
    else $arg.="0,";
  }
  if(isset($arg)&&($arg!="")){
    $ret="MAKETIME(".substr($arg,0,-1).")";
  }else $ret="";
  return $ret;  
}  
/**
 * Function _metaGroupFromGroup(group_id:int):Group
 * 
 */
function _metaGroupFromGroup($group_id) {
  global $db;
  $qry="SELECT * FROM `cw_group` WHERE `id`=".$group_id;
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    $ret=$arr["type"];
  }
  return $ret;
}

/******************************************************************************/
/***************            OUTGOING FUNCTIONS               ******************/
/******************************************************************************/
/**
 * Function _isPrevDenied(extension:string,ruleset:int,priority:int):boolean
 * return if outgoing condition has been previously denied or not
 */ 
function _isPrevDenied($extension,$ruleSetId,$priority){
  global $db;
  $deniedTrunks="";
  $deniedOnRules="";
  $deniedAll=false;
  // retrieve previous denies
  $qry="SELECT r.* , g.type , ge.`group_id`";
  $qry.=" FROM (cw_rule_outgoing AS r LEFT JOIN cw_group AS g ON r.`group` = g.`id`)";
  $qry.=" LEFT JOIN cw_group_entity AS ge ON r.`entity` = ge.`entity_id`";
  $qry.=" WHERE rule=".$ruleSetId."";
  $qry.=" AND r.`priority`<".$priority." AND r.`action`=".RULE_DENY."";
  $qry.=" AND ( r.`group`=".GROUP_OF_ALL." OR g.`type`=".GROUP_OF_PHONENUMBER." OR ge.`group_id`=".GROUP_OF_PHONENUMBER." )";
  $qry.=" ORDER BY r.`priority` DESC";
  $prev_rules=$db->Execute($qry);
  // analyse rules
  while ($rule=$prev_rules->FetchRow()) {
    $id=$rule["id"];
    $entity=$rule["entity"];
    $group=$rule["group"];
    $resurces=$rule["resource"];
    $timesegment=$rule["timesegment"];
    $deniedAll=false;
    // check time segment
    if(checkTime($timesegment)===false) continue;
    // check resurces
    switch ($group){
      // (All) or (All phone number)
      case GROUP_OF_ALL:          
      case GROUP_OF_PHONENUMBER: 
        if($resurces!="0"){
          $deniedTrunks.=_getTrunk($resurces)."&";
          $deniedOnRules.=$id." ";
        }else $deniedAll=true;   
      break;
      // (Class of numbers) or (Group of class of numbers)
      case 0:   
      default: 
        $included=_isIncluded($extension,$entity,$group,false);
        if($included===true){
          if($resurces!="0"){
            $deniedTrunks.=_getTrunk($resurces)."&";
            $deniedOnRules.=$id." ";
          }else $deniedAll=true;
        }
      break;  
    }
    if($deniedAll===true) break; 
  }
  if($deniedAll===true){
    $ret["all"]=true;
    $ret["list"]="";
    agiError("  Previously has been denied all");
  }else{
    if($deniedTrunks!="") $deniedTrunks=substr($deniedTrunks,0,-1);
    $ret["all"]=false;
    $ret["list"]=$deniedTrunks;
    if($deniedOnRules!=""){
      agiError("  Previously has been denied: ");
      agiError("    > on rules:  ".$deniedOnRules);
      agiError("    > on trunks: ".$deniedTrunks);    
    }
    else agiError("  Previously has never been denied");
  }
  return $ret;                  
}
/**
 * Function _getChannelFromEntity(entity_id:int):string
 * extract channel name from exstension entity 
 */ 
function _getChannelFromEntity($entity_id){
  global $db;
  $channelName="";
  //get channel name
  $qry="SELECT name FROM cw_channel WHERE `key`='".$entity_id."'";
  $res=$db->Execute($qry);
  if($res!==false){
    $channelArr=$res->FetchRow();
    $channelName=$channelArr["name"];
  }
  //return
  return $channelName;
}
/**
 * Function _getCid(trunk:int):array(string, string)
 * extract cid_name and cid_number setting for a given trunk 
 */ 
function _getCid($outTrunk){
  global $agi,$db;
  $cid_debug=array();
  $cid["cid_clir"]="0";
  $cid["cid_number"]="";
  $cid["cid_name"]="";
  $cid["status"]="ALLOWED";
  //get channel setting (clir could be set by an application)
  $channelClirArr=$agi->get_variable("CID_CLIR");
  $channelClir=trim($channelClirArr["data"]);
  // -----------------------------------
  // ----------   TRUNK CID   ----------
  // -----------------------------------  
  $qry="SELECT * ";
  $qry.="FROM `cw_channel` ";
  $qry.="WHERE name='".$outTrunk."'";
  $res=$db->Execute($qry);
  $outTrunkArr=$res->FetchRow();
  if(is_array($outTrunkArr)){
    if($outTrunkArr["enable_cid"]==1){
      if($outTrunkArr["force_cid"]==1){
        // -----------------------------------
        // ---------   FORCE CID   -----------
        // -----------------------------------
        if(($outTrunkArr["clir"]==1)&&(($channelClir=="")||($channelClir=="true"))){
          // Trunk is setted to 'force hide'
          agiError("  CLIR: ON (Set by trunk [Force ON])");
          $cid["cid_clir"]="1";
        }else if(($outTrunkArr["clir"]==0)&&(($channelClir=="")||($channelClir=="false"))){
          // Trunk is setted to 'force default'
          agiError("  CLIR: OFF (Set by trunk [Force ON])");
          $cid["cid_clir"]="0";
        }else{
          // Channel set isn't allowed: play congestion
          if($channelClir=="true") $cid["cid_clir"]="1"; else $cid["cid_clir"]="0";
          $cid["status"]="DENIED";
          return $cid;
        }
        $cid["cid_number"]=$outTrunkArr["cid_number"];
        $cid["cid_name"]=$outTrunkArr["cid_name"];
        //if cid_appexten is setted, append user extension to CID Number
        if($outTrunkArr["cid_appexten"]==1){
          $callerChannel=$agi->get_variable("CHANNEL");
          $callerChannelArr=split("-",$callerChannel["data"]);  
          $callerInfo=$callerChannelArr[0];
          $callerInfoArr=split("/",$callerInfo);
          $callerTech=$callerInfoArr[0];
          $callerExtension=$callerInfoArr[1];
          $cid["cid_number"].=$callerExtension;
          $cid_debug.="[append extension]";
        }
        agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by trunk [Force ON])");
        return $cid;
      }
      // Trunk is free: look user setting
      else {
        // -----------------------------------
        // ----------   USER CID   -----------
        // -----------------------------------
        $callerChannel=$agi->get_variable("CHANNEL");
        $callerChannelArr=split("-",$callerChannel["data"]);  
        $callerInfo=$callerChannelArr[0];
        $callerInfoArr=split("/",$callerInfo);
        $callerTech=$callerInfoArr[0];
        $callerExtension=$callerInfoArr[1];
        switch($callerTech){
          case "SIP":
          case "IAX2":
          default:
            break;
        }
        $qry="SELECT * ";
        $qry.="FROM `cw_cid` AS cid LEFT JOIN `cw_client` AS client ON cid.`extension` = client.`id` ";
        $qry.="WHERE client.`extension`='".$callerExtension."'";
        $res=$db->Execute($qry);
        $callerArr=$res->FetchRow();
        if(is_array($callerArr)){
          if($channelClir=="true"){
            agiError("  CLIR: ON (Set by channel [Force OFF])");
            $cid["cid_clir"]="1";
          }else if($channelClir=="false"){
            agiError("  CLIR: OFF (Set by channel [Force OFF])");
            $cid["cid_clir"]="0";
          }else if($callerArr["clir"]==1){
            // User is set to 'clir'
            agiError("  CLIR: ON (Set by user [Force OFF])");
            $cid["cid_clir"]="1";
          } else {
            // User is set 
            agiError("  CLIR: OFF (Set by user [Force OFF])");
            $cid["cid_clir"]="0";
          }
          $cid["cid_number"]=$callerArr["cid_number"];
          $cid["cid_name"]=$callerArr["cid_name"];
          agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by user [Force OFF])");
          return $cid;
        }
        // -----------------------------------
        // --------   NO USER CID   ----------
        // -----------------------------------  
        else{
          if($channelClir=="true"){
            agiError("  CLIR: ON (Set by channel [Force OFF])");
            $cid["cid_clir"]="1";
          }else if($channelClir=="false"){
            agiError("  CLIR: OFF (Set by channel [Force OFF])");
            $cid["cid_clir"]="0";
          }else if($outTrunkArr["clir"]==1){
            // Trunk default is 'hide'
            agiError("  CLIR: ON (Set by trunk [Force OFF])");
            $cid["cid_clir"]="1";
          }else{
            // Trunk default values
            agiError("  CLIR: OFF (Set by trunk [Force OFF])");
            $cid["cid_clir"]="0";
          }
          $cid["cid_number"]=$outTrunkArr["cid_number"];
          $cid["cid_name"]=$outTrunkArr["cid_name"];
          //if cid_appexten is setted, append user extension to CID Number
          if($outTrunkArr["cid_appexten"]==1){
            $callerChannel=$agi->get_variable("CHANNEL");
            $callerChannelArr=split("-",$callerChannel["data"]);  
            $callerInfo=$callerChannelArr[0];
            $callerInfoArr=split("/",$callerInfo);
            $callerTech=$callerInfoArr[0];
            $callerExtension=$callerInfoArr[1];
            $cid["cid_number"].=$callerExtension;
            $cid_debug.="[append extension]";
          }
          agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Set by trunk [Force OFF])");
          return $cid;
        }
      }
    }
    // -----------------------------------
    // ----   PASSTHROUGH USER CID   -----
    // ----------------------------------- 
    else{
      $cidNameArr=$agi->get_variable("CALLERID(name)");
      $cidNumArr=$agi->get_variable("CALLERID(num)");
      $cid["cid_name"]=$cidNameArr["data"];
      $cid["cid_number"]=$cidNumArr["data"];
      if($channelClir=="true"){
        agiError("  CLIR: ON (Set by channel [Force OFF])");
        $cid["cid_clir"]="1";
      }else if($channelClir=="false"){
        agiError("  CLIR: OFF (Set by channel [Force OFF])");
        $cid["cid_clir"]="0";
      }else if($outTrunkArr["clir"]==1){
        $cid["cid_clir"]="1";
        agiError("  CLIR: ON (Set by trunk)");
      }else{
        $cid["cid_clir"]="0";
        agiError("  CLIR: OFF (Set by trunk)");
      }
      agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Passthrough set by trunk)");
      return $cid;
    }
  }
  // Error processing cid
  agiError("  CLIR: OFF (Error processing cid)");
  agiError("  CALLER ID: ".$cid["cid_name"]." <".$cid["cid_number"]."> (Error processing cid)");
  return $cid;
}
/**
 * Function _chanIsAvail(trunklist:string):string
 * return the first available trunk between trunks on list 
 */
function _chanIsAvail($availableTrunks){
  global $agi;
  //give up mISDN layers
  $trunksArr=split("&",$availableTrunks);
  if(is_array($trunksArr)){
  	foreach($trunksArr as $trunk){
  		list($tTech,$tPG)=split("/",$trunk);
      if($tTech=="mISDN"){
  			agiError("  Wake up mISDN layers for Port Group $tPG");
        $agi->exec("misdn_check_l2l1 $tPG|2");
      }
    }
  }
  //select an available channel
  $agi->exec("ChanIsAvail $availableTrunks");
  $ret=$agi->get_variable("AVAILCHAN");
  if($ret!=""){
    $selectedChannelArr=split("-",$ret["data"]);
    if(is_array($selectedChannelArr)){ 
      $selectedChannel=$selectedChannelArr[0];
      return $selectedChannel;
    }
    else return "";
  }
  else return "";
}
/**
 * Function _getTrunkFromChannel(channel:string):string
 * extract the trunk from the channel 
 */ 
function _getTrunkFromChannel($outChannel){
  global $db;
  $outChannelArr=split("-",$outChannel);  
  $outTrunk=$outChannelArr[0];
  $outTrunkArr=split("/",$outTrunk);
  $outTech=$outTrunkArr[0];
  $outExtension=$outTrunkArr[1];
  switch($outTech){
    case "mISDN":
      $qry="SELECT g.name ";
      $qry.="FROM `cw_isdn_portgroup` AS pg LEFT JOIN `cw_isdn_group` AS g ON pg.`group` = g.`id` ";
      $qry.="WHERE pg.port=".$outExtension."";
      $res=$db->Execute($qry);
      if($res!==false){
        $isdnGroup=$res->FetchRow();
        if(is_array($isdnGroup)) $outExtension="g:".$isdnGroup["name"];
        else return "";
      }else return "";
      break;
    case "Zap":
      $qry="SELECT g.`group` ";
      $qry.="FROM `cw_zapata_group` AS g ";
      $qry.="JOIN  `cw_zapata_portgroup` AS pg ON pg.`group`=g.`id` ";
      $qry.="WHERE pg.port=".$outExtension."";
      $res=$db->Execute($qry);
      if($res!==false){
        $zapGroup=$res->FetchRow();
        if(is_array($zapGroup)) $outExtension="g".$zapGroup["group"];
        else return "";
      }else return "";
      break;
    case "SIP":
    case "IAX2":
    default:
      break;
  }
  $outTrunkName=$outTech."/".$outExtension;
  return $outTrunkName;
}
/**
 * Function _getDiffChannel(permittedList:string,deniedList: string):string
 * return the difference between permitted and denied trunks
 */ 
function _getDiffChannel($permitted,$denied){
  $permittedArr=split("&",$permitted);  
  $deniedArr=split("&",$denied);  
  $diff=array_diff($permittedArr,$deniedArr);
  $ret="";
  foreach($diff as $ch){
    $ret.=$ch."&";
  }     
  $ret=substr($ret, 0, -1);
  return $ret;
}
/**
 * Function _isLocalCall(channel: string):boolean
 * return if the caller channel is "Local" or not
 */ 
function _isLocalCall($channel){
  global $db;
  $channelArr=split("/",$channel);
  if($channelArr[0]=="Local") return true;
  else return false;
}

/******************************************************************************/
/***************            INCOMING FUNCTIONS               ******************/
/******************************************************************************/
/**
 * Function _getCallInfo(channel:String):array(int, string)
 * extract from incoming call the channel type and the called number 
 */ 
function _getCallInfo($channel){
  global $db,$agi;
  $ret=array("channelTech" => "", "calledNumber" => "");
  //Extract channel type
  $retrievedCalledChannelArr=split("/",$channel);
  if(is_array($retrievedCalledChannelArr)){
    $channelType = $retrievedCalledChannelArr[0];
    //Extract called number
    if($channelType=="SIP"){ //SIP CALL
      $sipToHeader=$agi->get_variable("SIP_HEADER(TO)");
      $sipFromHeader=$agi->get_variable("SIP_HEADER(FROM)");
      if($sipToHeader["data"]!=""){
        // TO HEADER
        if((strpos($sipToHeader["data"], "<")!==false)&&(strpos($sipToHeader["data"], ">")!==false)  ){
          //sintax type 1: <sip:xxxxxxxxxx@xxx.xxx.xxx.xxx>
          $sipTo=substr($sipToHeader["data"], 0, strpos($sipToHeader["data"], ">")).">";
          $calledNumber=substr($sipTo, (strpos($sipTo,":") + 1), ((strpos($sipTo,"@")) - (strpos($sipTo,":")) - 1));
        }else{
          //sintax type 2: sip:xxxxxxxxxx@xxx.xxx.xxx.xxx
          $sipTo=$sipToHeader["data"];
          $calledNumber=substr($sipTo, (strpos($sipTo,":") + 1), ((strpos($sipTo,"@")) - (strpos($sipTo,":")) - 1));
        }
        // FROM HEADER
        if((strpos($sipFromHeader["data"], "<")!==false)&&(strpos($sipFromHeader["data"], ">")!==false)){
          //sintax type 1: <sip:xxxxxxxxxx@xxx.xxx.xxx.xxx> 
          $sipFrom=substr($sipFromHeader["data"], 0, strpos($sipFromHeader["data"], ">")).">";  
        }else{
          //sintax type 2: sip:xxxxxxxxxx@xxx.xxx.xxx.xxx
          $sipFrom=$sipFromHeader["data"];
        } 
        agiError("[SIP CALL]");
        agiError("   FROM: ".$sipFrom);
        agiError("   TO: ".$sipTo);
        agiError("   CALLED: ".$calledNumber);
      }
      $ret["channelTech"]=SIP;
      $ret["calledNumber"]=$calledNumber;
    } 
  }
  return $ret;
}
/**
 * Function _getAccountInfo(channelId:int,$calledNumber:String):array(int, string, int, string, string)
 * extract provider account's informations
 */ 
function _getCalledAccount($channelId,$calledNumber){
  global $db;
  $calledMatch=false;   
  //verify type
  $qry="SELECT ch.* , en.`type` ";
  $qry.="FROM `cw_channel` AS ch JOIN `cw_entity` AS en ON ch.`key` = en.`id` ";
  $qry.="WHERE ch.`id`=$channelId";
  $res=$db->Execute($qry);
  if($res!==false){ 
    $ch=$res->FetchRow();
    if($ch["type"]==PROVIDER_ACCOUNT){
      //get all relative provider's accounts
      $qry="SELECT * ";
      $qry.="FROM `cw_provider_account` AS pa JOIN `cw_channel` AS ch ON pa.`id` = ch.`key`";
      $qry.="WHERE pa.`provider`=(SELECT `provider` FROM `cw_provider_account` WHERE `id`=".$ch["key"].")";
      $res=$db->Execute($qry);
      //find called account
      while(($accArr=$res->FetchRow())&&($calledMatch===false)){
        $accNumbers=split("\|",$accArr["msns"]);
        if(is_array($accNumbers)){ 
          foreach($accNumbers as $accNumber){
            $calledMatch=_matchPattern($calledNumber,$accNumber);
            if($calledMatch===true){
              $ret["id"]=$accArr["key"];
              $ret["name"]=$accArr["name"];
              $ret["channel"]=$accArr["id"];
              $ret["msns"]=$accArr["msns"];
              $ret["rule"]=$accArr["rule"];
              break;
            }
          }
        }
      }
    }
  }
  return $ret;
}
/**
 * Function _isCallerIncluded(channelId:int,$calledNumber:String):boolean
 * return if the caller match with the caller specified in an incoming rule
 */ 
function _isCallerIncluded($callerid,$callertype,$triggeredCallerid){
  global $db,$agi;
  $match=false;
  switch ($callertype) {
    case CALLER_ALL:
      agiError("    --> match for all pattern");
      $match=true;
    break;
    case CALLER_ANONYMOUS:
      if($triggeredCallerid=="ANONYMOUS") {
        agiError("    --> search for pattern ANONYMOUS : ! MATCH !");
        $match=true;
      } else { 
        $match=false;
        agiError("    --> search for pattern ANONYMOUS : NO MATCH");
      }
    break;
    case CALLER_ID:
      if($triggeredCallerid==$callerid){
        agiError("    --> search for pattern $callerid : ! MATCH !");
        $match=true;
      } else {
        agiError("    --> search for pattern $callerid : NO MATCH");
        $match=false;
      }
    break;
    case CALLER_CLASS:
      $match=_isIncluded($triggeredCallerid,$callerid,0,true);
    break;
    case CALLER_GROUP:
      $match=_isIncluded($triggeredCallerid,0,$callerid,true);
    break;
    default:
      agiError("ERROR: unknown callertype ($callertype) in incoming.inc");
      $match=false;
    break;
  }
  return $match;
}
/**
 * Function _getCallerCidName(callernumber:String):String
 * return the caller name which match the caller number in contact list
 * and is set to authoritative
 */ 
function _getCallerCidName($callernumber){
  global $db;
  $callernumber=trim($callernumber);
  if($callernumber!=""){
  	$qry="SELECT cid_name ";
		$qry.="FROM `cw_contacts` ";
    $qry.="WHERE `read`=1 ";
    $qry.="AND ( ".
    	"((`phonenumber` LIKE '".$callernumber."') AND (`phonenumber_on_cid`=1)) ".
    	"OR ((`mobile` LIKE '".$callernumber."') AND (`mobile_on_cid`=1)) ".
      ") ";
    $qry.="LIMIT 1 ";
    $res=$db->Execute($qry);
    if(($res!==false)&&($c=$res->FetchRow())){
      return trim($c["cid_name"]);
    }
  }
  return "";
}
/**
 * Function _verifyJump(ruleset:String):array(id:int,name:string)
 * return the target of the jump of the rulest. If no jump is set, return an
 * empty array
 */ 
function _verifyJump($ruleset){
  global $db;
  $ret=array();
  //get jump
	$qry="SELECT rj.*,r.`name` ";
  $qry.="FROM `cw_rule_jump` AS rj ";
  $qry.="LEFT JOIN `cw_rule` AS r ON r.`id`=rj.`ruleset_to` ";
  $qry.="WHERE rj.`ruleset_from`='".$ruleset."' ";
  $res=$db->Execute($qry);
  if($res!==false){
    if($arr=$res->FetchRow()){
      //verify jump validity
      $qry="SELECT * FROM `cw_rule_jump` WHERE id='".$arr["id"]."' AND NOW()<'".$arr["end_of_life"]."'";
      $res=$db->Execute($qry);
      if($res!==false){
        $q=$res->RecordCount();
        if($q>0){
          return array(
              "id"=>$arr["ruleset_to"],
              "name"=>$arr["name"],
              "until"=>$arr["end_of_life"]
            );
        }else{
          $qry="DELETE FROM `cw_rule_jump` WHERE id='".$arr["id"]."'";
          $db->Execute($qry);
        }
      }
    }
  }
  return "";
}
/******************************************************************************/
/******************            IVR FUNCTIONS               ********************/
/******************************************************************************/
/**
 * Function _getIvrNode(id:int):Array
 * 
 */
function _getIvrNode($id){
  global $db;
  $ret=array();
  //get node structure
  $qry="SELECT * FROM cw_ivr WHERE id=".$id;
  $res=$db->Execute($qry);
  if($res!==false){
    $ret=$res->FetchRow();
    //add children to node structure
    $ret["children"]=array();
    $qry="SELECT * FROM cw_ivr WHERE parent=".$id." ORDER BY `order`";
    $res=$db->Execute($qry);
    if($res!==false){
      while($arr=$res->FetchRow()){
        $ret["children"][]=$arr;
      }
    }
  }  
  return $ret;
}
/**
 * Function _getAudioFilePath(id:int):String
 * 
 */
function _getAudioFilePath($id){
  global $db;
  $qry="SELECT * FROM cw_sounds_file WHERE id=".$id;
  $res=$db->Execute($qry);
  if($res!==false){
    $arr=$res->FetchRow();
    $ret=$arr["filepath"];
  }
  return $ret;
}
      
/******************************************************************************/
/****************            SHARED FUNCTIONS               *******************/
/******************************************************************************/
/**
 * Function _isIncluded(extension:string, entity:int, group:int):boolean
 * 
 */
function _isIncluded($extension,$entity,$group,$output=true){
  global $db;
  $included=false;
  if($entity > 0){ // class of number
    $qry="SELECT * FROM cw_number WHERE id=".$entity;
    $numbers=$db->Execute($qry);
    while($row=$numbers->FetchRow()){
      $patternArr=split("\|",$row["number"]);
      foreach($patternArr as $pattern){
        $included=_matchPattern($extension,$pattern);
        if($included===true) {
          if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
          break;       
        }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
      }
    }
  }else if($group > 0) { // group of class of numbers
    if(($group!=1)&&($group!=GROUP_OF_PHONENUMBER)){ 
      $qry="SELECT * FROM `cw_group_entity` WHERE `group_id`=".$group;
      $classes=$db->Execute($qry);
      while($row=$classes->FetchRow()){
        $qry="SELECT * FROM cw_number WHERE id=".$row["entity_id"];
        $numbers=$db->Execute($qry);
        while($row=$numbers->FetchRow()){
          $patternArr=split("\|",$row["number"]);
          foreach($patternArr as $pattern){
            $included=_matchPattern($extension,$pattern);
            if($included===true) {
              if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
              break;       
            }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
          }
        }
        if($included===true) break;  
      }
    }else{ //$group==1 --> destination * || group==5 --> destination ALL_PHONENUMBERS
      $included=true; 
      if($output===true) agiError("    --> match for all pattern");
    }
  }
  return $included;
}
/**
 * Function _matchPattern(extension:string, pattern:string):boolean
 * 
 */ 
function _matchPattern($ext,$pattern){
  $debug=false;
  // pattern is a match case (return false if different)
  if($pattern[0]!='_'){
    if($pattern==$ext) return true;
    else return false;
  }
  // pattern is a regular expression (return false at the first difference)
  if($debug===true)agiError("(Pattern) $pattern <==> $ext (Number)");
  $pattern=substr($pattern,1);  // remove "_"  
  $pat_pos=0;
  $ext_pos=0;
  while($pat_pos<strlen($pattern)){
    switch($pattern[$pat_pos]){
        case "0":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
        case "6":
        case "7":
        case "8":
        case "9":
          if($pattern[$pat_pos]!=$ext[$ext_pos]) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "X":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(0,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "Z":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(1,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "N":
          if((!isset($ext[$ext_pos]))||($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(2,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break; 
        case "[":
          if($debug===true) agiError("    |> \"[\""); 
          $remaining=substr($pattern,$pat_pos);
          $end_pos=strpos($remaining,"]");
          if($end_pos===false){
            agiError("PARSING ERROR: check pattern definition ($pattern)");
            return false;
          }
          $p_content=substr($remaining,1,($end_pos)); 
          $p_found=false;
          $p_end=false;
          $j=0;
          while(($j<(strlen($p_content)-1))&&($p_found===false)){
            $next=$j+1;
            switch($p_content[$next]){
              case "0":
              case "1":
              case "2":
              case "3":
              case "4":
              case "5":
              case "6":
              case "7":
              case "8":
              case "9":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "]":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "-":
                $range_start=$j;
                $range_end=($j+2);
                if(in_array($ext[$ext_pos],range($p_content[$range_start],$p_content[$range_end]))) $p_found=true;
                if($p_content[($j+3)]=="]") $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." in [".$p_content[$range_start]."-".$p_content[$range_end]."]");
                $j=($j+3); 
                break;
              default:
                agiError("PARSING ERROR: check pattern definition ($pattern)");
                return false;
                break;
            }
            if($p_end===true) break;
          }
          if($p_found===false) return false;
          $pat_pos=($pat_pos+(strlen($p_content))+1);
          $ext_pos++;
          if($debug===true) agiError("    |> \"]\""); 
          break;  
        case ".":
          if($ext[$ext_pos]=="") return false;
          if($debug===true) agiError("    |> \".\""); 
          return true;
          break;
        default:
          agiError("PARSING ERROR: check pattern definition ($pattern)");
          return false;
          break;
     }
  }
  if($debug===true) agiError("EXIT: if  ".strlen($ext)." == ".$ext_pos."  --> MATCH"); 
  if(strlen($ext)==$ext_pos) return true;
  return false;
}
/**
 * Function agiError(string:String):int
 * print string to php's error log 
 */  
function agiError($string){
  global $agiDebug,$agi;
  if($agiDebug===true){ 
    $id=$agi->get_variable("UNIQUEID");
    $string="[".$id["data"]."] ".$string;
    error_log($string);
    return 1;
  }
  else return 0; 
}
if(!function_exists("errArr")){
  function errArr($arr){
    if (count($arr)==0) error_log("ok!");
    else {
      ob_start();
        print_r($arr);
      $contents=ob_get_contents();
      ob_end_clean();
      error_log($contents);
    }
    return;
  }
}
?>